package main

import "fmt"

func main() {
	//1.go的逃逸分析是在编译时完成的，不是运行时
	//2.如果变量在函数外部被引用，那么一定分配到堆上
	//3.如果变量在函数外部无引用，一般会优先分配到栈上
	//可以通过 go build -gcflags -m 查看编译时的逃逸分析结果

	//case1()

	//case2()

	//case3()

	case4()
}

func case1() {
	//变量的类型不确定会导致变量逃逸到堆上
	num := 10
	//.\main.go:18:13: num escapes to heap
	fmt.Println(num)
	//之所以num会逃逸到heap，是因为Println的参数是interface{}类型的可变参数，interface{}编译期间无法确定类型
}

func case2() *int {
	//返回局部变量指针到外部 逃逸到堆上
	num := 10
	//.\main.go:21:13: num escapes to heap
	return &num
}

func case3() {
	//变量的大小不确定也会逃逸到堆上
	count := 10
	//make([]struct {}, count) escapes to heap
	tmp := make([]struct{}, count)
	for i := 0; i < len(tmp); i++ {
		fmt.Println(i)
	}
	//count是变量，导致编译时无法确定大小
}

func case4() {
	//变量过大 为了不导致栈溢出 需要分配到堆上
	//goroutine其实是用户态线程，其栈也是用户栈，本质上也是从堆分配的。但是是需要当做栈来看待的，本身有大小限制
	//大小限制见runtime/stack.go

	//make([]int64, 1000000) escapes to heap
	tmp := make([]int64, 1000000)
	//tmp := make([]struct{}, 1000000)//不会因为过大而逃逸，因为空结构体占用大小为0
	for i := 0; i < len(tmp); i++ {
		fmt.Println(i)
	}
}
